package net.gdface.sdk;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;

import net.gdface.annotation.DeriveMethod;
import net.gdface.annotation.GenericParam;
import net.gdface.image.ImageErrorException;
import net.gdface.image.MatType;

/**
 * 
 * 人脸识别SDK核心接口<br>
 * 此接口中mat前缀的方法图像数据参数为图像矩阵(要求图像宽高必须为偶数),
 * 其他方法中所有byte[]类型的图像参数都指未解码的图像格式(如jpg,bmp,png...),<br>
 * 目前支持的图像格式类型依赖于JDK的支持能力
 * @author guyadong
 */
public interface FaceApi {

	/**
	 * 对人脸图像提取特征码，返回比较相似度结果<br>
	 * imgData1和imgData2相等或imgData2为{@code null}时,即比较同一张图像中的两张人脸的相似度
	 * 调用该方法时假设图像({@code imgData1}和{@code imgData2})能正常解码,<br>
	 * 所以当对图像解码出现异常时，将{@link ImageErrorException}异常对象封装到{@link RuntimeException}抛出<br>
	 * 任何参数为{@code null}则抛出{@link IllegalArgumentException}
	 * @param imgData1
	 *            　图像1数据(jpg,png...)字节数组
	 * @param facePos1
	 *            检测到的人脸/眼睛位置
	 * @param imgData2
	 *            　图像1数据(jpg,png...)字节数组
	 * @param facePos2
	 *            检测到的人脸/眼睛位置
	 * @return 两张人脸之间的相似度(0.0~1)
	 * @throws NotFaceDetectedException
	 * @throws ImageErrorException
	 * @see #getCodeInfo(byte[], int, CodeInfo[])
	 * @see #compareCode(byte[], byte[])
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public abstract double compare2Face(
			@GenericParam(name="T1")byte[] imgData1, 
			CodeInfo facePos1, 
			@GenericParam(name="T2")byte[] imgData2, 
			CodeInfo facePos2)
			throws NotFaceDetectedException, ImageErrorException;

	/**
	 * 对两个图像指定范围({@link FRect})进行人脸检测,找到并提取唯一的人脸特征码,然后比较相似度,返回相似度结果<br>
	 * imgData1和imgData2相等或imgData2为{@code null}时,
	 * 即比较同一张图像中的两张人脸的相似度,这种情况下detectRect1和detectRect2都不能为{@code null}<br>
	 * imgData1和imgData2不相等且都不为{@code null}时,detectRect1和detectRect2被忽略
	 * 
	 * @param imgData1
	 *            图像1数据(jpg,png...)字节数组,为{@code null}则抛出{@link IllegalArgumentException}
	 * @param detectRect1
	 *            图片检测范围，为null时全图检测
	 * @param imgData2
	 *            图像2数据(jpg,png...)字节数组
	 * @param detectRect2
	 *            图片检测范围，为null时全图检测
	 * @return 两张人脸之间的相似度(0.0~1)
	 * @throws ImageErrorException
	 * @throws NotFaceDetectedException
	 * @see #detectAndGetCodeInfo(byte[], int)
	 * @see #compareCode(byte[], byte[])
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public abstract double detectAndCompare2Face(
			@GenericParam(name="T1")byte[] imgData1, 
			FRect detectRect1, 
			@GenericParam(name="T2")byte[] imgData2, 
			FRect detectRect2)
			throws ImageErrorException, NotFaceDetectedException;

	/**
	 * 特征码比对<br>
	 * 参数为{@code null}或长度不一致则抛出{@link IllegalArgumentException}异常,返回两个特征码之间的相似度（0.0~1）
	 * @param code1 待比对的特征码
	 * @param code2 待比对的特征码
	 * @return 相似度（0.0~1）
	 */
	public abstract double compareCode(@GenericParam(name="T1")byte[] code1, @GenericParam(name="T2")byte[] code2);

	/**
	 * 特征码比对1:N<br>
	 * 返回对应的特征码相似度数组
	 * @param code1 待比对的特征码
	 * @param codes 包含人脸特征的{@link CodeInfo }数组
	 * @return 特征码相似度数组
	 */
	public abstract double[] compareCodes(byte[] code1, CodeInfo[] codes);
	/**
	 * 特征码比对1:N<br>
	 * 对{@code imgData}图像数据检测人脸并提取特征，然后与{@code code}特征进行比对,
	 * 返回包含比对相似度结果的{@link CompareResult}实例
	 * @param code 人脸特征数据
	 * @param imgData 待比对的图像(jpg,png...)(可能有多张人脸)
	 * @param faceNum  参见 {@link #getCodeInfo(byte[], int, CodeInfo[])}
	 * @return {@link CompareResult}实例
	 * @throws NotFaceDetectedException 没有检测到人脸
	 * @throws ImageErrorException
	 * @see #compareCodes(byte[], CodeInfo[])
	 * @see #detectAndGetCodeInfo(byte[], int)
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	CompareResult compareFaces(
			@GenericParam(name="T1")byte[] code, 
			@GenericParam(name="T2")byte[] imgData, 
			int faceNum)
	throws NotFaceDetectedException, ImageErrorException;

	/**
	 * 特征码比对1:N<br>
	 * @param code1 人脸特征数据
	 * @param codes 一组人脸特征
	 * @return 返回对应的特征码相似度列表
	 */
	public abstract List<Double> compareFeatures(byte[] code1, List<byte[]> codes);

	/**
	 * 先对图像数据{@code imgData}进行人脸检测，然后提取人脸特征码<br>
	 * 返回所有成功提取特征码的{@link CodeInfo}数组,
	 * 与{@link #getCodeInfo(byte[], int, CodeInfo[])}的返回结果有差别，返回结果中不包含检测到人脸但提取特征码失败的对象
	 * @param imgData
	 *            图像数据(jpg,png...)字节数组,为{@code null}则抛出{@link IllegalArgumentException}
	 * @param faceNum
	 *            参见 {@link #getCodeInfo(byte[], int, CodeInfo[])}
	 * @return {@link CodeInfo}数组<br>
	 * @throws ImageErrorException
	 * @throws NotFaceDetectedException 没有检测人脸或提取到特征码的人脸数目不是要求的人脸数目(faceNum>0)
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public abstract CodeInfo[] detectAndGetCodeInfo(byte[] imgData, int faceNum)
			throws ImageErrorException, NotFaceDetectedException;
	/**
	 * 检测指定的人脸是否戴口罩<br>
	 * 返回值有三种状态:
	 * <ul>
	 * <li>{@code true} 戴口罩返回</li>
	 * <li>{@code false} 未戴口罩返回</li>
	 * <li>{@code null} 不知道</li>
	 * </ul>
	 * @param imgData  图像数据(jpg,png...)字节数组,为{@code null}则抛出 {@link IllegalArgumentException} 
	 * @param faceInfo  可见光图像人脸信息
	 * @return 返回口罩检测结果
	 * @throws ImageErrorException 
	 * @since 2.1.9
	 */
	public Boolean wearMask(byte[] imgData, CodeInfo faceInfo) throws ImageErrorException;

	/**
	 * 对图像({@code imgData})进行人脸检测，返回人脸位置数据对象{@link CodeInfo}数组<br>
	 * 没有检测到人脸则返回空数组
	 * @param imgData
	 *            图像数据(jpg,png...)字节数组,为{@code null}则抛出{@link IllegalArgumentException}
	 * @return {@link CodeInfo}数组
	 * @throws ImageErrorException 图像读取错误
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public abstract CodeInfo[] detectFace(byte[] imgData) throws ImageErrorException;

	/**
	 * 检测最中心的人脸<br>
	 * 返回人脸位置数据对象{@link CodeInfo}
	 * @param imgData 图像数据(jpg,png...)字节数组,为{@code null}则抛出{@link IllegalArgumentException}
	 * @return {@link CodeInfo}
	 * @throws NotFaceDetected 没有检测到人脸
	 * @throws ImageError 图像读取错误
	 */
	public CodeInfo detectCenterFace(byte[] imgData) throws NotFaceDetectedException, ImageErrorException;
	/**
	 * 检测最大的人脸<br>
	 * 返回人脸位置数据对象{@link CodeInfo}
	 * @param imgData 图像数据(jpg,png...)字节数组,为{@code null}则抛出{@link IllegalArgumentException}
	 * @return {@link CodeInfo}
	 * @throws NotFaceDetected 没有检测到人脸
	 * @throws ImageError 图像读取错误
	 */
	public CodeInfo detectMaxFace(byte[] imgData) throws NotFaceDetectedException, ImageErrorException;

	/**
	 * 根据{@code facePos}提供的人脸信息位置, 在{@code imgData}图像中提取特征码<br>
	 * 与 {@link #detectAndGetCodeInfo(byte[], int)}不同, 本方法不对图像数据{@code imgData}进行人脸检测,<br>
	 * 假设{@code facePos}是 {@link #detectFace(byte[])}或 {@link #detectFaceAgain(byte[], FRect[])} 的返回结果<br>
	 * 返回facePos,如果没有提取到特征码,则对应元素{@link CodeInfo#getCode()}返回{@code null}
	 * @param imgData
	 *           图像数据(jpg,png...)字节数组,为{@code null}则抛出 {@link IllegalArgumentException} 
	 * @param faceNum
	 *            要求返回的人脸特征码数目<br>
	 *            大于0时，如果实际提取到的人脸特征码数目不等于{@code faceNum},则抛出{@link NotFaceDetectedException}<br>
	 *            小于等于0时，返回所有提取到到人脸特征码 
	 * @param facePos
	 *            检测到的人脸位置对象列表<br>
	 *            为{@code null}或数组长度为0时抛出{@link IllegalArgumentException}<br>
	 *            如果元素为{@code null},则跳过<br>
	 * @return 返回facePos
	 * @throws NotFaceDetectedException 提取特征码的人脸数目为0或没有提取到指定数目(faceNum大于0时)的特征码
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public abstract CodeInfo[] getCodeInfo(byte[] imgData, int faceNum, CodeInfo[] facePos) throws NotFaceDetectedException;

	/**
	 * 根据{@code facePos}提供的人脸信息位置, 在{@code imgData}图像中提取特征码<br>
	 * 返回包含人脸特征的{@link CodeInfo}对象，提取特征失败则返回{@code null}
	 * @param imgData  图像数据(jpg,png...)字节数组,为{@code null}则抛出 {@link IllegalArgumentException} 
	 * @param facePos 人脸位置信息对象，为{@code null}则抛出异常
	 * @return {@link CodeInfo} or {@code null}
	 */
	@DeriveMethod(methodSuffix="Single",localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public CodeInfo getCodeInfo(byte[] imgData, CodeInfo facePos);

	/**
	 * 多张人脸提取特征(用于多张人脸合成一个特征的算法)<br>
	 * 返回人脸特征数据
	 * {@link #multiFaceFeature()}返回{@code false}时代表此方法未被实现，执行会抛出异常
	 * @param faces 人脸图像数据(jpg,png...)与人脸位置信息对象的映射
	 * @return 人脸特征数据
	 * @throws NotFaceDetectedException 
	 */
	public abstract byte[] getFeature(Map<ByteBuffer,CodeInfo>faces) throws NotFaceDetectedException;
 	/**
	 * 判断图像是否能提取到人脸特征码<br>
	 * 为{@code true}则能检测到人脸并能提取特征码
	 * @param imgData
	 *            图像数据(jpg,png...)字节数组,为{@code null}则抛出{@link IllegalArgumentException}
	 * @return 为{@code true}则能检测到人脸并能提取特征码
	 * @throws ImageErrorException
	 * @see #detectAndGetCodeInfo(byte[], int)
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public abstract boolean hasFace(byte[] imgData) throws ImageErrorException;

	/**
	 * 1:N 人脸特征搜索<br>
	 * 搜索与{@code code}相似度大于{@code similarty}的记录(最多返回前{@code rows}个结果)<br>
	 *  返回包含相似度计算结果的{@link FseResult}数组,返回结果以相似度降序排列<br>
	 *  相似度值通过 {@link FseResult#getSimilartys()} 获取<br>
	 *  对应的人脸特征ID由 {@link FseResult#getFeatureIds()}获取
	 * @param feature
	 *            要搜索的特征码
	 * @param similarty
	 *            相似度阀值
	 * @param rows
	 *            最多返回的记录数目
	 * @return 包含相似度计算结果的{@link FseResult}数组<br>
	 * @since 3.0.0
	 * @throws UnsupportedOperationException 没有人脸识别算法(FaceApi实例)支持
	 */
	public FseResult[] searchFeatures(byte[] feature, double similarty, int rows);

	/**
	 * 1:N 人脸图像搜索<br>
	 * 对{@code facePos}指定的人脸位置提取特征码,然后在数据库中搜索相似的人脸返回搜索结果。<br>
	 * 返回包含相似度计算结果的FseResult数组<br>
	 * 相似度值通过 FseResult.getSimilartys() 获取<br>
	 * 对应的人脸特征ID由 FseResult.getFeatureIds()获取<br>
	 * 
	 * @param imgData
	 *            图片字节数组
	 * @param facePos
	 *            人脸位置对象<br>
	 *            为{@code null}时,先做人脸检测再提取特征码<br>
	 *            不为{@code null}时,直接在指定的位置提取特征码<br>
	 * @param similarty 相似度阀值
	 * @param rows 最多返回的记录数目
	 * @return 包含相似度计算结果的{@link FseResult}数组<br>
	 * @since 3.0.0
	 * @throws NotFaceDetectedException -
	 * @throws ImageErrorException -
	 * @throws UnsupportedOperationException 没有人脸识别算法(FaceApi实例)支持
	 * @see #getCodeInfo(byte[], int, CodeInfo[])
	 * @see #searchFeatures(byte[], double, int)
	 */
	@DeriveMethod(localResolvedTypes={InputStream.class,URL.class,File.class,ByteBuffer.class})
	public FseResult[] searchFaces(byte[] imgData, CodeInfo facePos, double similarty, int rows) throws NotFaceDetectedException, ImageErrorException;

	/**
	 * 对图像矩阵进行人脸检测<br>
	 * 返回人脸信息对象列表，没有检测到人脸返回空表
	 * @param matType 图像矩阵类型
	 * @param matData 图像矩阵
	 * @param width 图像宽度
	 * @param height 图像高度
	 * @return 人脸信息对象列表
	 * @since 2.1.9
	 */
	@DeriveMethod(localResolvedTypes={ByteBuffer.class})
	public CodeInfo[] matDetectFace(MatType matType, byte[] matData, int width, int height);
	/**
	 * 检测最大的人脸<br>
	 * 返回人脸位置数据对象{@link CodeInfo}
	 * @param imgData 图像数据(jpg,png...)字节数组,为{@code null}则抛出{@link IllegalArgumentException}
	 * @return {@link CodeInfo}
	 * @throws NotFaceDetected 没有检测到人脸
	 * @since 2.1.9
	 */
	@DeriveMethod(localResolvedTypes={ByteBuffer.class})
	public CodeInfo matDetectMaxFace(MatType matType, byte[] matData, int width, int height) throws NotFaceDetectedException;
	/**
	 * 根据{@code facePos}提供的人脸信息位置, 在图像矩阵中提取特征码<br>
	 * 返回包含人脸特征的{@link CodeInfo}对象，提取特征失败则返回{@code null}
	 * @param matType 图像矩阵类型
	 * @param matData 图像矩阵
	 * @param width 图像宽度
	 * @param height 图像高度
	 * @param facePos 人脸位置信息对象，为{@code null}则抛出异常
	 * @return {@link CodeInfo} or {@code null}
	 */
	@DeriveMethod(methodSuffix="Single",localResolvedTypes={ByteBuffer.class})
	public CodeInfo matGetCodeInfo(MatType matType, byte[] matData, int width, int height, CodeInfo facePos);

	/**
	 * 根据facePos提供的人脸信息位置, 在图像矩阵中提取特征码<br>
	 * 包含人脸特征数据的facePos
	 * @param matType 图像矩阵类型
	 * @param matData 图像矩阵
	 * @param width 图像宽度
	 * @param height 图像高度
	 * @param facenum 
	 *            要求返回的人脸特征码数目<br>
	 *            大于0时，如果实际提取到的人脸特征码数目不等于{@code faceNum},则抛出{@link NotFaceDetectedException}<br>
	 *            小于等于0时，返回所有提取到到人脸特征码 	 
	 * @param facePos 人脸位置信息对象,不可为{@code null}
	 * @return always facePos
	 * @throws NotFaceDetectedException 提取特征码的人脸数目为0或没有提取到指定数目(faceNum大于0时)的特征码
	 * @since 2.1.9
	 */
	@DeriveMethod(localResolvedTypes={ByteBuffer.class})
	public CodeInfo[] matGetCodeInfo(MatType matType, byte[] matData, int width, int height, int facenum, CodeInfo[] facePos) throws NotFaceDetectedException;
	/**
	 * 先对图像矩阵数据{@code imgData}进行人脸检测，然后提取人脸特征码<br>
	 * 返回包含人脸特征数据的{@code CodeInfo}数组
	 * @param matType 图像矩阵类型
	 * @param matData 图像矩阵
	 * @param width 图像宽度
	 * @param height 图像高度
	 * @param faceNum 参见 {@link #getCodeInfo(byte[], int, CodeInfo[])}
	 * @return {@code CodeInfo}数组
	 * @throws NotFaceDetectedException
	 * @since 2.1.9
	 */
	@DeriveMethod(localResolvedTypes={ByteBuffer.class})
	public CodeInfo[] matDetectAndGetCodeInfo(MatType matType, byte[] matData, int width, int height, int faceNum)	throws NotFaceDetectedException;
	/**
     * 检测指定的人脸是否戴口罩<br>
	 * 返回值有三种状态:
	 * <ul>
	 * <li>{@code true} 戴口罩返回</li>
	 * <li>{@code false} 未戴口罩返回</li>
	 * <li>{@code null} 不知道</li>
	 * </ul>
	 * @param matType 图像矩阵类型
	 * @param matData  图像矩阵
	 * @param width  图像宽度
	 * @param height 图像高度
	 * @param faceInfo  人脸位置信息
     * @return 返回口罩检测结果
     * @since 2.1.9
     */
	@DeriveMethod(localResolvedTypes={ByteBuffer.class})
	public Boolean matWearMask(MatType matType, byte[] matData, int width, int height, CodeInfo faceInfo);
	/**
	 * 判断图像矩阵是否能提取到人脸特征码<br>
	 * 返回为{@code true}则能检测到人脸并能提取特征码
	 * @param matType 图像矩阵类型
	 * @param matData 图像矩阵,为{@code null}则抛出{@link IllegalArgumentException}
	 * @param width  图像宽度
	 * @param height 图像高度
	 * @return 为{@code true}则能检测到人脸并能提取特征码
	 * @since 2.1.9
	 */
	@DeriveMethod(localResolvedTypes={ByteBuffer.class})
	boolean matHasFace(MatType matType, byte[] matData, int width, int height);

	/**
	 * 1:N 人脸图像搜索<br>
	 * 对{@code facePos}指定的人脸位置提取特征码,然后在数据库中搜索相似的人脸返回搜索结果。<br>
	 * 返回包含相似度计算结果的FseResult数组<br>
	 * 相似度值通过 FseResult.getSimilartys() 获取<br>
	 * 对应的人脸特征ID由 FseResult.getFeatureIds()获取<br>
	 * @param matType 图像矩阵类型
	 * @param matData 图像矩阵
	 * @param width 图像宽度
	 * @param facePos
	 *            人脸位置对象<br>
	 *            为{@code null}时,先做人脸检测再提取特征码<br>
	 *            不为{@code null}时,直接在指定的位置提取特征码<br>
	 * @param similarty 相似度阀值
	 * @param rows 最多返回的记录数目
	 * @return 包含相似度计算结果的{@link FseResult}数组<br>
	 * @since 3.0.0
	 * @throws NotFaceDetectedException -
	 * @throws ImageErrorException -
	 * @throws UnsupportedOperationException 没有人脸识别算法(FaceApi实例)支持
	 * @see #getCodeInfo(byte[], int, CodeInfo[])
	 * @see #searchFeatures(byte[], double, int)
	 */
	@DeriveMethod(localResolvedTypes={ByteBuffer.class})
	public FseResult[] matSearchFaces(MatType matType, byte[] matData, int width, int height, CodeInfo facePos, double similarty, int rows)
	throws NotFaceDetectedException, ImageErrorException;

	/**
	 * 返回当前SDK特性(能力)描述,已定义字段:<br>
	 * <ul>
	 * <li>SDK_VERSION [string]SDK版本号</li>
	 * <li>MULTI_FACE_FEATURE [boolean]算法是否支持多人脸合成特征</li>
	 * <li>FACE_LIVE [boolean]是否支持活体检测</li>
	 * <li>WEAR_MASK [boolean]是否支持口罩检测</li>
	 * <li>FDDATA_SIZE [int]人脸检测数据的(byte)长度</li>
	 * <li>FEATURE_SIZE [int]人脸特征数据(byte)长度</li>
	 * <li>MAX_FACE_COUNT [int]最大检测人脸数目</li>
	 * <li>FSE_ENABLE [boolean]是否支持特征内存搜索引擎</li>
	 * <li>CODEINFO_RELOCATE [boolean]是否支持CodeInfo对象重定位</li>
	 * <li>LOCAL_DETECT [boolean] 人脸检测是否为本地实现,未定义则为false</li>
	 * </ul>
	 * 以上字段名常量定义参见{@link CapacityFieldConstant}
	 * @return key-value 对描述算法能力的映射
	 * @since 2.2.7
	 */
	public Map<String, String> sdkCapacity();

	/**
	 * 返回当前接口实现方式<br> 
	 * false WebService类型<br>
	 * true 本地实现
	 * @return　boolean
	 */
	public abstract boolean isLocal();
}